
/*
  ____     ____               _______    __   __  _    _                   
 |  _ \   / __ \      /\     |__   __|   \ \ / / | |  | |                  
 | |_) | | |  | |    /  \       | |       \ V /  | |  | |                  
 |  _ <  | |  | |   / /\ \      | |        > <   | |  | |                  
 | |_) | | |__| |  / ____ \     | |       / . \  | |__| |                  
 |____/   \____/  /_/    \_\    |_|      /_/ \_\  \____/                   
   _____    ____    ______   _______     ______   _    _    _____    _____ 
  / ____|  / __ \  |  ____| |__   __|   |  ____| | |  | |  / ____|  / ____|
 | (___   | |  | | | |__       | |      | |__    | |__| | | (___   | (___  
  \___ \  | |  | | |  __|      | |      |  __|   |  __  |  \___ \   \___ \ 
  ____) | | |__| | | |         | |      | |      | |  | |  ____) |  ____) |
 |_____/   \____/  |_|         |_|      |_|      |_|  |_| |_____/  |_____/ 
                                                                           
*/

/*
 *  Description: BOAT XU SOFT FHSS
 *  Version: V1.00
 *  Author: boat xu
 *  Email: xifengzui@outlook.com
 *  Date: 2020-07-10 07:54:10
 *  LastEditTime: 2020-07-22 15:25:01
 */ 
#include "bsf_types.h"
#include "bsf_itypes.h"
#include "bsf_rand.h"
#include "bsf_payload.h"
#include "bsf_timer.h"
#include "bsf.h"
#include "bsf_misc_com.h"
#include "bsf_debug.h"
bsf_IsPktArrival_Func   IsPktArrival = b_null;
bsf_SendPkt_Func        SendPkt = b_null;
bsf_GetPkt_Func         GetPkt = b_null;
bsf_SendingStatus_Func  SendingPktStatus = b_null;
bsf_SetFreq_Func        SetFreq = b_null;
bsf_GetCurrentTimestamp_Func GetCurrentTimestamp = b_null;
bsf_OnConnOkEvent       OnConnOkEvent = b_null;
bsf_OnConnLostEvent     OnConnLostEvent = b_null;
bsf_PktSendResultEvent  PktSendResultEvent = b_null;


#if BSF_MASTER 

enum
{
    t_work_initDefaultFreqTable = 0,
    t_work_jumpToNextFreq,
    t_work_trySync,
    t_work_ackCheck_Sync,
    t_work_timestampReset_resetWeight,
    t_work_TxRxOperator,
    t_work_ackCheck_payloadTx,
    t_max_stat
}t_statMachine;

typedef struct
{
    b_u8_t state;//状态字 1字节
    b_u8_t isOnInitialState;//
    b_u8_t isOnSeedTableState;//
    b_u8_t isAllowSendPktDrop;
    b_u8_t isAllowRecvPktDrop;
    b_u8_t retryCnt;
    bsf_payload   *sendPldPtr;
    b_pktSendRetStat pktSendRetStat;
    bsf_payload   *recvPldPtr;
    b_pktRecvStat pktRecvStat;
}t_stateMachineContext;

static bsf_payload  pld;//global;
static state states[t_max_stat];
static t_stateMachineContext tctx;

/**
 *  Description: for more info please see the State Machine
 *  Param: 
 *  Return: 
 */
void work_initDefaultFreqTable(void)
{
    //TODO generate defualt table
    //dprintf("CALL: work_initDefaultFreqTable");
    bsf_freqHopping_init(b_null);//using b_null parms to generate the default table
    bsf_timer_resetTimestamp(b_tt_all);//jump then reset time stamp;
    tctx.isOnInitialState = b_true;
    tctx.state = t_work_jumpToNextFreq;
    //TODO RESET SYNC TIMEOUT
    
}

void work_trySync(void)
{
    //dprintf("CALL: work_trySync");
    bsf_miscComFillSync(&pld);
    SendPkt(pld.flatBuf,pld.stru.head.totalSize);//发送数据包
    tctx.state = t_work_ackCheck_Sync;
    bsf_timer_resetTimestamp(b_tt_ackTimeout);
}

void work_ackCheck_Sync(void)
{
    //dprintf("CALL: work_ackCheck_Sync");
    switch(SendingPktStatus())
    {
        case b_pss_Idel:
        case b_pss_CompletedNoAck:
        {
            tctx.state = t_work_trySync;
            break;
        }
        case b_pss_CompletedAck:
        {
            tctx.state = t_work_timestampReset_resetWeight;
            bsf_timer_resetTimestamp(b_tt_fhss);//ack check ok ,must reset timestamp .
            tctx.isOnInitialState = b_false;
            if(OnConnOkEvent != NULL)
            {
                OnConnOkEvent();
            }
            break;
        }
        case b_pss_Busy:
        // DO nothing unitl pass the ackcheck status;
        {
            if(bsf_timer_isSelectTimestampGreateThan(b_tt_ackTimeout,FHSS_MASTER_ACK_TIMEOUT_CNT))
            {
                //that is a hardware error you shold debug and find why
                // do nothing
            }
            break;
        }
        
        default:
        {
           //!Do Nothing on wrong state,shoud debug and find why
           break;
        }
    }
    // when system is not on initial state, the system will check the abnormal timeout
    // if find the abnormal timeout == FHSS_MASTER_ABNORMAL_TIMER_CNT
    // the system will reset to default table,and looplly send initial table.

    if((tctx.isOnInitialState == b_false) && (bsf_timer_isSelectTimestampGreateThan(b_tt_fhss,FHSS_MASTER_ABNORMAL_TIMER_CNT) == b_true))
    {
        tctx.state = t_work_initDefaultFreqTable;

        if(OnConnLostEvent != b_null)
        {
            OnConnLostEvent();
        }
        
    }
    else if(bsf_timer_isSelectTimestampGreateThan(b_tt_fhss,FHSS_MASTER_TIMER_CNT) == b_true)
    {
        tctx.state = t_work_jumpToNextFreq;
    }
    
}

void work_jumpToNextFreq(void)
{
    bsf_timer_resetTimestamp(b_tt_fhss);
    //dprintf("CALL: work_jumpToNextFreq");
    if(tctx.isOnInitialState == b_true)
    {
        bsf_freqHopping_jumpToNext_loop();
    }
    else
    {
        bsf_freqHopping_jumpToNext();
    }
    tctx.state = t_work_trySync;
}

void work_timestampReset_resetWeight(void)
{
    //dprintf("CALL: work_timestampReset_resetWeight");
    bsf_freqHopping_recoverWeight();
    // timestamp reset, except tabTimeout.
    tctx.isOnInitialState = b_false;

    if(bsf_timer_isSelectTimestampGreateThan(b_tt_tabTimeout,FHSS_MASTER_TABTIMEOUT_CNT) && (tctx.isOnSeedTableState == b_false))
    {
        tctx.isOnSeedTableState = b_true;
    }
    
    tctx.state = t_work_TxRxOperator;
 
}

void work_TxRxOperator(void)
{
    bsf_payload *ppld = b_null;
    b_boolean ret;
    b_u8_t retLen;
    //dprintf("CALL: work_TxRxOperator");
    // check recv packet,b_rxidel state will set at user read the RxBuffer empty
    if(IsPktArrival() == b_true)
    {
        ret = bsf_payloadPushRxBuf_Begin(&ppld);
        if(ret == b_false)
        {
            tctx.pktRecvStat = b_prs_RecvOverflow;//rx buffer overflow, will lost the newerst packet
            bsf_payloadPushRxBuf_End(ret);
        }
        else
        {
            tctx.pktRecvStat = b_prs_RecvExist;
            retLen = GetPkt((b_u8_t *)ppld,BSF_PACKSIZE);
            //TODO Length Match Set
            ret = bsf_payloadInfoCheckIsOk(ppld);
            //INFO check
            if(ret == b_false)
            {
                dprintf("[ERROR][BSF] packet formate error.");
                bsf_payloadPushRxBuf_End(b_false);
            }
            //
            if(retLen != ppld->stru.head.totalSize)
            {
                dprintf("[ERROR][BSF] not a complete packet, might lost some data.");
                 bsf_payloadPushRxBuf_End(b_false);
            } 
            else
            {
                bsf_payloadPushRxBuf_End(b_true);
            }
            
           
        }
        
        if(bsf_timer_isSelectTimestampGreateThan(b_tt_fhss,FHSS_MASTER_TIMER_CNT) == b_true)
        {
            //dprintf("    On Rx state timestamp timeout, freq jump");
            tctx.state = t_work_jumpToNextFreq;
        }
    }
    
    if(tctx.isOnSeedTableState == b_true)
    {
       // dprintf("        isOnSeedTableStat = b_true,sending table");
        bsf_miscComFillSeedTable(&pld,bsf_freqHopping_getSeed(),bsf_freqHopping_getShadowTable());
        SendPkt(pld.flatBuf,pld.stru.head.totalSize);
        bsf_timer_resetTimestamp(b_tt_ackTimeout);
        tctx.state = t_work_ackCheck_payloadTx;
    }
    else
    {
        //dprintf("        isOnSeedTableStat = b_false,normally sending data.");
        ret = bsf_payloadPopTxBuf_Begin(&ppld);
        if(ret == b_true)
        {
            tctx.sendPldPtr = ppld;
            tctx.retryCnt = 0;
            SendPkt(ppld->flatBuf,ppld->stru.head.totalSize);
            bsf_timer_resetTimestamp(b_tt_ackTimeout);
            //bsf_payloadPopTxBuf_End(b_true);
            tctx.state = t_work_ackCheck_payloadTx;
            
        }
        else
        {
            bsf_payloadPopTxBuf_End(b_false);
            if(bsf_timer_isSelectTimestampGreateThan(b_tt_fhss,FHSS_MASTER_TIMER_CNT) == b_true)
            {
                //dprintf("    On Tx IDEL timestamp timeout, freq jump");
                tctx.state = t_work_jumpToNextFreq;
            }
        }
    }
    
}

void work_ackCheck_payloadTx(void)
{
    bsf_pktSendResult result;
    // dprintf("CALL: work_ackCheck_payloadTx");
    //TODO if there is busy, wait, until failed.
    switch(SendingPktStatus())
    {
        case b_pss_Idel:
        {
            break;
            tctx.state = t_work_TxRxOperator;
        }
        case b_pss_CompletedNoAck:
        {
            bsf_freqHopping_lowerWeight();
            if(tctx.isAllowSendPktDrop)
            {
                 bsf_payloadPopTxBuf_End(b_true);
            }
           else
           {
               tctx.retryCnt++;
               if(tctx.retryCnt > SEND_RETRY_CNT)
               {
                   bsf_payloadPopTxBuf_End(b_true);
               }
               else
               {
                   bsf_payloadPopTxBuf_End(b_false);
                   
                    if(PktSendResultEvent!=b_null)
                    {
                        result.index = 0;
                        result.index = ((b_u16_t)(((bsf_MiscComUserData *)tctx.sendPldPtr->stru.payload)->indexH)) <<8;
                        result.index |= ((bsf_MiscComUserData *)tctx.sendPldPtr->stru.payload)->indexL;
                        result.sendResult = b_psrs_SendFailed;
                        if(PktSendResultEvent != b_null)
                        {
                            PktSendResultEvent(&result);
                        }
                        
                    }
               }
           }



            tctx.state = t_work_TxRxOperator;
            break;
        }
        case b_pss_CompletedAck:
        {
            if(tctx.isOnSeedTableState == b_true)
            {
                tctx.isOnSeedTableState = b_false;
                bsf_freqHopping_setToShadowTable();
            }
            else
            {
                
                if(PktSendResultEvent!= b_null)
                {
                    result.index = ((b_u16_t)(((bsf_MiscComUserData *)tctx.sendPldPtr->stru.payload)->indexH)) <<8;
                    result.index |= ((bsf_MiscComUserData *)tctx.sendPldPtr->stru.payload)->indexL;
                    result.sendResult = b_psrs_SendOk;
                    PktSendResultEvent(&result);
                }
                
                bsf_payloadPopTxBuf_End(b_true);//release tx packet here if not success ,sys will keep stuck until success
            }
            
            tctx.state = t_work_TxRxOperator;
            break;
        }
        case b_pss_Busy:
        {
            if(bsf_timer_isSelectTimestampGreateThan(b_tt_ackTimeout,FHSS_MASTER_ACK_TIMEOUT_CNT))
            {
                //that is a hardware error you shold debug and find why
                // do nothing
            }
            break;
        }
        default:
        {
            // do notihng
            break;
        }
    }
    if(bsf_timer_isSelectTimestampGreateThan(b_tt_fhss,FHSS_MASTER_TIMER_CNT) == b_true)
    {
         tctx.state = t_work_jumpToNextFreq;
    }
}

void bsf_init(void)
{
    b_u8_t ii =0;
    b_u8_t *p;
    p = (b_u8_t *)&tctx; 
    //dprintf("CALL: bsf_init");
    bsf_payload_init();

    for(ii=0;ii<sizeof(t_stateMachineContext);ii++)
    {
        p[ii] = 0;
    }
    tctx.state = t_work_initDefaultFreqTable;
    states[t_work_initDefaultFreqTable] = work_initDefaultFreqTable;
    states[t_work_jumpToNextFreq] = work_jumpToNextFreq;
    states[t_work_trySync] = work_trySync;
    states[t_work_ackCheck_Sync] = work_ackCheck_Sync;
    states[t_work_timestampReset_resetWeight] = work_timestampReset_resetWeight;
    states[t_work_TxRxOperator] = work_TxRxOperator;
    states[t_work_ackCheck_payloadTx] = work_ackCheck_payloadTx;
}

/**
 *  Description: main job please put it into a loop
 *  Param: 
 *  Return: 
 */
void bsf_do(void)
{
    states[tctx.state]();
    #if DPRINT
    switch (tctx.state)
    {
        case t_work_initDefaultFreqTable:
        {
            dprintf("STAT1  default table init.");
            break;
        }
        case t_work_jumpToNextFreq:
        {
            dprintf("STAT2  jump to next freq.");
            break;
        }
        case t_work_trySync:
        {
            dprintf("STAT3  try sync.");
            break;
        }
        case t_work_ackCheck_Sync:
        {
            dprintf("STAT4  sync ack check.");
            break;
        }
        case t_work_timestampReset_resetWeight:
        {
            dprintf("STAT5  timestamp reset weight reset.");
            break;
        }
        case t_work_TxRxOperator:
        {
            dprintf("STAT6  tx rx.");
            break;
        }
        case t_work_ackCheck_payloadTx:
        {
            dprintf("STAT7  payload tx.");
            break;
        }
        
        default:
            break;
    }   
    #endif
}
#endif

#if BSF_SLAVER





#endif



















/**
 *  Description: in order to boost the write efficiency.
 *  you can simplely use this to get the direct write buffer address.direct operat the memory.
 *  please be sure Begin And End Pair, if not , it might cause unknow bug.
 *  Param: index: your define of packet index, your data need to be send ,if putLen == 0 ,the data will be ignore. buffer will keep free
 *  its not a mult thread  function ,please be aware when you use it into mult thread function.
 *  Return: 
 */
bsf_payload *sendPld = b_null;
b_boolean bsf_sendPkt_Begin(b_u8_t **outBuf)
{
    b_boolean ret = b_false;
    bsf_do();
    if(sendPld != b_null)
    {
        return b_false;
    }
    ret =  bsf_payloadPushTXBuf_Begin(&sendPld);
    if(ret == b_true)
    {
        *outBuf = ((bsf_MiscComUserData *)sendPld->stru.payload)->userData;
    }
    else
    {
        sendPld = b_null;
    }
    
    return ret;
    
}
void bsf_sendPkt_End(b_u16_t index,b_u8_t putLen)
{
    bsf_do();
    if(sendPld !=b_null)
    {
        bsf_miscComFillUserData(sendPld,index,b_null,putLen);
        bsf_payloadPushTXBuf_End(b_true);
        sendPld = b_null;
    }
}

/**
 *  Description:  in order to boost the read efficiency.
 *  you can simplely use this to get the direct read buffer address.direct operat the memory.
 *  please be sure Begin And End Pair, if not , it might cause unknow bug.and 
 *  Param:  outIndex: the index that user put in ,
 *          outBuf: the addres of the buffer.
 *          isGetFinished: the flag that indecate the buffer,is not for me, will keep stay in buffer,until next one read it.
 *  Return: 
 */
bsf_payload *recvPld = b_null;
b_boolean bsf_readPkt_Begin(unsigned short int *outIndex,unsigned char **outBuf)
{
    b_boolean ret = b_false;
    bsf_do();
    if(recvPld != b_null)
    {
        return b_false;
    }
    ret = bsf_payloadPopRxBuf_Begin(&recvPld);
    if(ret == b_true)
    {
       *outBuf = recvPld->stru.payload;
       *outIndex = 0;
       *outIndex = ((b_u16_t)((bsf_MiscComUserData *)recvPld->stru.payload)->indexH)<<8;
       *outIndex |= (b_u16_t)((bsf_MiscComUserData *)recvPld->stru.payload)->indexL;
    }
    else
    {
        recvPld = b_null;
    }
    return ret;
}

void bsf_readPkt_End(b_boolean isGetFinished)
{
    bsf_do();
    if(recvPld !=b_null)
    {
        bsf_payloadPopRxBuf_End(isGetFinished);
        recvPld = b_null;
    }
}

/**
 *  Description: is allow packet drop,some times the system might need to send packet as fast as possible,so will allow some loss packet.
 *  Param: default system will allow pkt drop.
 *  Return: 
 */
void bsf_sendPkt_isAllowPktDrop(b_boolean allowFlag)
{
    tctx.isAllowSendPktDrop = allowFlag;
}
